#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "msg.h"
#include "train_trans.h"
#include "trans_file.h"

int send_file(const int net_fd, const char *filename, const char *real_path) {
  /* printf("send file\n"); */
  send_msg(net_fd, filename);

  int file_fd = open(real_path, O_RDWR);
  off_t file_size = -1;
  if (file_fd == -1) {
    perror("open");
    train_send(net_fd, &file_size, sizeof(file_size));
    return -1;
  }

  struct stat statbuf;
  fstat(file_fd, &statbuf);
  file_size = statbuf.st_size;
  printf("file size = %ld\n", file_size);
  train_send(net_fd, &statbuf.st_size, sizeof(statbuf.st_size));

  off_t offset = 0;
  train_recv(net_fd, &offset, sizeof(offset));
  printf("send_file offset = %ld\n", offset);
  off_t oft = lseek(file_fd, offset, SEEK_SET);
  if (oft == -1) {
    perror("lseek");
  }
  /* printf("oft = %ld\n", oft); */

  char buf[1000] = {0};
  if (file_size < 100 * 1024 * 1024) {
    while (1) {
      bzero(buf, sizeof(buf));
      ssize_t sret = read(file_fd, buf, sizeof(buf));
      train_send(net_fd, buf, sret);
      if (sret == 0) {
        break;
      }
    }
  } else {
    char *p = (char *)mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED,
                           file_fd, 0);
    if (!p) {
      perror("mmap");
    }
    send(net_fd, p + offset, file_size - offset, MSG_NOSIGNAL);
    munmap(p, file_size);
    /* send(net_fd, "", 0, MSG_NOSIGNAL); */
  }

  printf("send file done!\n");

  close(file_fd);
  return 0;
}

void print_progress(float progress) {
  printf("[");
  int cur = progress / 2;
  for (int i = 0; i < 50; ++i) {
    if (i < cur) {
      printf("=");
    } else if (i == cur) {
      printf(">");
    } else {
      printf(" ");
    }
  }
  printf("]");
  printf(" %5.2lf%%\r", progress);
}

int recv_file(const int net_fd, const char *save_name) {
  /* printf("recv filename\n"); */

  char filename[512] = {0};
  recv_msg(net_fd, filename);

  /* printf("recv filesize\n"); */
  off_t file_size = -1;
  train_recv(net_fd, &file_size, sizeof(file_size));
  if (file_size == -1) {
    printf("no such file!\n");
    return -1;
  }

  int fd = open(save_name, O_RDWR | O_CREAT, 0666);

  off_t offset = lseek(fd, 0, SEEK_END);
  /* printf("recv side offset = %ld\n", offset); */
  if (offset == -1) {
    perror("lseek");
  }

  train_send(net_fd, &offset, sizeof(offset));

  // TODO: 进度条
  off_t cursize = offset;
  off_t lastsize = 0;
  off_t slice = file_size / 10000;

  char buf[1000];
  if (file_size < 100 * 1024 * 1024) {
    while (1) {
      bzero(buf, sizeof(buf));
      int ret = train_recv(net_fd, buf, sizeof(buf));
      if (ret == 0) {
        break;
      }
      write(fd, buf, ret);
      cursize += ret;
      /* printf("recv size = %ld\n", cursize); */
      // TODO: 进度条
      if (cursize - lastsize > slice) {
        print_progress(100.0 * cursize / file_size);
        fflush(stdout);
        lastsize = cursize;
      }
    }
  } else {
    /* ftruncate(fd, file_size); */
    /* char *p = (char *)mmap(NULL, file_size, PROT_READ | PROT_WRITE,
     * MAP_SHARED, */
    /*                        fd, offset); */
    /* recvn(net_fd, p, file_size); */
    char buf[4096];
    int ret;
    while (cursize < file_size) {
      /* ret = recv(net_fd, p + cursize, file_size - cursize, MSG_NOSIGNAL); */
      /* printf("ret = %d\n", ret); */
      /* if (ret == -1) { */
      /*   perror("recv"); */
      /* } */
      /* if (ret == 0) { */
      /*   break; */
      /* } */
      /* cursize += ret; */
      /* printf("cursize = %ld, file_size = %ld\n", cursize, file_size); */
      /* printf("cursize = %ld, file_size = %ld\n", cursize, file_size); */
      ret = recv(net_fd, buf, sizeof(buf), MSG_NOSIGNAL);
      if (ret == -1) {
        perror("recv");
      }
      if (ret == 0) {
        break;
      }
      cursize += ret;
      /* printf("cursize = %ld, file_size = %ld\n", cursize, file_size); */
      write(fd, buf, ret);
      if (cursize - lastsize > slice) {
        print_progress(100.0 * cursize / file_size);
        fflush(stdout);
        lastsize = cursize;
      }
    }
    /* munmap(p, file_size); */
  }
  printf("\ndone!\n");

  close(fd);
  return 0;
}
